﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using System.Runtime.ConstrainedExecution;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;

namespace SystemHelperLibrary.DeviceExt
{
	[Flags()]
	internal enum SetupDiGetClassDevsFlags
	{
		Default = 1,
		Present = 2,
		AllClasses = 4,
		Profile = 8,
		DeviceInterface = (int)0x10
	}

	internal enum DiFunction
	{
		SelectDevice = 1,
		InstallDevice = 2,
		AssignResources = 3,
		Properties = 4,
		Remove = 5,
		FirstTimeSetup = 6,
		FoundDevice = 7,
		SelectClassDrivers = 8,
		ValidateClassDrivers = 9,
		InstallClassDrivers = (int)0xa,
		CalcDiskSpace = (int)0xb,
		DestroyPrivateData = (int)0xc,
		ValidateDriver = (int)0xd,
		Detect = (int)0xf,
		InstallWizard = (int)0x10,
		DestroyWizardData = (int)0x11,
		PropertyChange = (int)0x12,
		EnableClass = (int)0x13,
		DetectVerify = (int)0x14,
		InstallDeviceFiles = (int)0x15,
		UnRemove = (int)0x16,
		SelectBestCompatDrv = (int)0x17,
		AllowInstall = (int)0x18,
		RegisterDevice = (int)0x19,
		NewDeviceWizardPreSelect = (int)0x1a,
		NewDeviceWizardSelect = (int)0x1b,
		NewDeviceWizardPreAnalyze = (int)0x1c,
		NewDeviceWizardPostAnalyze = (int)0x1d,
		NewDeviceWizardFinishInstall = (int)0x1e,
		Unused1 = (int)0x1f,
		InstallInterfaces = (int)0x20,
		DetectCancel = (int)0x21,
		RegisterCoInstallers = (int)0x22,
		AddPropertyPageAdvanced = (int)0x23,
		AddPropertyPageBasic = (int)0x24,
		Reserved1 = (int)0x25,
		Troubleshooter = (int)0x26,
		PowerMessageWake = (int)0x27,
		AddRemotePropertyPageAdvanced = (int)0x28,
		UpdateDriverUI = (int)0x29,
		Reserved2 = (int)0x30
	}

	internal enum StateChangeAction
	{
		Enable = 1,
		Disable = 2,
		PropChange = 3,
		Start = 4,
		Stop = 5
	}

	[Flags()]
	internal enum Scopes
	{
		Global = 1,
		ConfigSpecific = 2,
		ConfigGeneral = 4
	}

	internal enum SetupApiError
	{
		NoAssociatedClass = unchecked((int)0xe0000200), ClassMismatch = unchecked((int)0xe0000201),
		DuplicateFound = unchecked((int)0xe0000202),
		NoDriverSelected = unchecked((int)0xe0000203),
		KeyDoesNotExist = unchecked((int)0xe0000204),
		InvalidDevinstName = unchecked((int)0xe0000205),
		InvalidClass = unchecked((int)0xe0000206),
		DevinstAlreadyExists = unchecked((int)0xe0000207),
		DevinfoNotRegistered = unchecked((int)0xe0000208),
		InvalidRegProperty = unchecked((int)0xe0000209),
		NoInf = unchecked((int)0xe000020a),
		NoSuchHDevinst = unchecked((int)0xe000020b),
		CantLoadClassIcon = unchecked((int)0xe000020c),
		InvalidClassInstaller = unchecked((int)0xe000020d),
		DiDoDefault = unchecked((int)0xe000020e),
		DiNoFileCopy = unchecked((int)0xe000020f),
		InvalidHwProfile = unchecked((int)0xe0000210),
		NoDeviceSelected = unchecked((int)0xe0000211),
		DevinfolistLocked = unchecked((int)0xe0000212),
		DevinfodataLocked = unchecked((int)0xe0000213),
		DiBadPath = unchecked((int)0xe0000214),
		NoClassInstallParams = unchecked((int)0xe0000215),
		FileQueueLocked = unchecked((int)0xe0000216),
		BadServiceInstallSect = unchecked((int)0xe0000217),
		NoClassDriverList = unchecked((int)0xe0000218),
		NoAssociatedService = unchecked((int)0xe0000219),
		NoDefaultDeviceInterface = unchecked((int)0xe000021a),
		DeviceInterfaceActive = unchecked((int)0xe000021b),
		DeviceInterfaceRemoved = unchecked((int)0xe000021c),
		BadInterfaceInstallSect = unchecked((int)0xe000021d),
		NoSuchInterfaceClass = unchecked((int)0xe000021e),
		InvalidReferenceString = unchecked((int)0xe000021f),
		InvalidMachineName = unchecked((int)0xe0000220),
		RemoteCommFailure = unchecked((int)0xe0000221),
		MachineUnavailable = unchecked((int)0xe0000222),
		NoConfigMgrServices = unchecked((int)0xe0000223),
		InvalidPropPageProvider = unchecked((int)0xe0000224),
		NoSuchDeviceInterface = unchecked((int)0xe0000225),
		DiPostProcessingRequired = unchecked((int)0xe0000226),
		InvalidCOInstaller = unchecked((int)0xe0000227), NoCompatDrivers = unchecked((int)0xe0000228),
		NoDeviceIcon = unchecked((int)0xe0000229), InvalidInfLogConfig = unchecked((int)0xe000022a),
		DiDontInstall = unchecked((int)0xe000022b), InvalidFilterDriver = unchecked((int)0xe000022c),
		NonWindowsNTDriver = unchecked((int)0xe000022d), NonWindowsDriver = unchecked((int)0xe000022e),
		NoCatalogForOemInf = unchecked((int)0xe000022f), DevInstallQueueNonNative = unchecked((int)0xe0000230),
		NotDisableable = unchecked((int)0xe0000231), CantRemoveDevinst = unchecked((int)0xe0000232),
		InvalidTarget = unchecked((int)0xe0000233), DriverNonNative = unchecked((int)0xe0000234),
		InWow64 = unchecked((int)0xe0000235), SetSystemRestorePoint = unchecked((int)0xe0000236),
		IncorrectlyCopiedInf = unchecked((int)0xe0000237), SceDisabled = unchecked((int)0xe0000238),
		UnknownException = unchecked((int)0xe0000239), PnpRegistryError = unchecked((int)0xe000023a),
		RemoteRequestUnsupported = unchecked((int)0xe000023b), NotAnInstalledOemInf = unchecked((int)0xe000023c),
		InfInUseByDevices = unchecked((int)0xe000023d), DiFunctionObsolete = unchecked((int)0xe000023e),
		NoAuthenticodeCatalog = unchecked((int)0xe000023f), AuthenticodeDisallowed = unchecked((int)0xe0000240),
		AuthenticodeTrustedPublisher = unchecked((int)0xe0000241), AuthenticodeTrustNotEstablished = unchecked((int)0xe0000242),
		AuthenticodePublisherNotTrusted = unchecked((int)0xe0000243), SignatureOSAttributeMismatch = unchecked((int)0xe0000244),
		OnlyValidateViaAuthenticode = unchecked((int)0xe0000245)
	}

	[StructLayout(LayoutKind.Sequential)]
	internal struct DeviceInfoData
	{
		public int Size;
		public Guid ClassGuid;
		public int DevInst;
		public IntPtr Reserved;
	}

	[StructLayout(LayoutKind.Sequential)]
	internal struct PropertyChangeParameters
	{
		public int Size;
		// part of header. It's flattened out into 1 structure.      
		public DiFunction DiFunction;
		public StateChangeAction StateChange;
		public Scopes Scope;
		public int HwProfile;
	}

	internal class NativeMethods
	{
		//USB Raw Device {a5dcbf10-6530-11d2-901f-00c04fb951ed}
		//Disk Device {53f56307-b6bf-11d0-94f2-00a0c91efb8b}
		//Network Card {ad498944-762f-11d0-8dcb-00c04fc3358c}
		//Human Interface Device (HID) {4d1e55b2-f16f-11cf-88cb-001111000030}
		//Palm {784126bf-4190-11d4-b5c2-00c04f687a67} 

		private NativeMethods()
		{
		}

		[DllImport("user32.dll", SetLastError = true)]
		internal static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);

		[DllImport("user32.dll", SetLastError = true)]
		internal static extern bool UnregisterDeviceNotification(IntPtr Handle);

		[DllImport("setupapi.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool SetupDiCallClassInstaller(DiFunction installFunction, SafeDeviceInfoSetHandle deviceInfoSet, [In()] ref DeviceInfoData deviceInfoData);

		[DllImport("setupapi.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool SetupDiEnumDeviceInfo(SafeDeviceInfoSetHandle deviceInfoSet, int memberIndex, ref DeviceInfoData deviceInfoData);

		[DllImport("setupapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)]
		public static extern SafeDeviceInfoSetHandle SetupDiGetClassDevs([In()] ref Guid classGuid, [MarshalAs(UnmanagedType.LPWStr)] string enumerator, IntPtr hwndParent, SetupDiGetClassDevsFlags flags);

		/* [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)]    
		 * [return: MarshalAs(UnmanagedType.Bool)]      
		 * public static extern bool SetupDiGetDeviceInstanceId(SafeDeviceInfoSetHandle deviceInfoSet, [In()] ref DeviceInfoData did, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder deviceInstanceId, int deviceInstanceIdSize, [Out()] ref int requiredSize);    
		 */

		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref DeviceInfoData did, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize);

		[SuppressUnmanagedCodeSecurity()]
		[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
		[DllImport("setupapi.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);

		[DllImport("setupapi.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool SetupDiSetClassInstallParams(SafeDeviceInfoSetHandle deviceInfoSet, [In()] ref DeviceInfoData deviceInfoData, [In()] ref PropertyChangeParameters classInstallParams, int classInstallParamsSize);
	}

	internal class SafeDeviceInfoSetHandle : SafeHandleZeroOrMinusOneIsInvalid
	{
		public SafeDeviceInfoSetHandle()
			: base(true)
		{
		}

		protected override bool ReleaseHandle()
		{
			return NativeMethods.SetupDiDestroyDeviceInfoList(this.handle);
		}
	}

	public sealed class DeviceHelper
	{
		private DeviceHelper()
		{
		}

		/// <summary>         
		/// Enable or disable a device.       
		/// </summary>    
		///  <param name="classGuid">The class guid of the device. Available in the device manager.</param>        
		///  /// <param name="instanceId">The device instance id of the device. Available in the device manager.</param>         
		/// <param name="enable">True to enable, False to disable.</param>         
		/// <remarks>Will throw an exception if the device is not Disableable.</remarks>        
		public static void SetDeviceEnabled(Guid classGuid, string instanceId, bool enable)
		{
			SafeDeviceInfoSetHandle diSetHandle = null;
			try
			{
				// Get the handle to a device information set for all devices matching classGuid that are present on the system.
				diSetHandle = NativeMethods.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, SetupDiGetClassDevsFlags.Present);
				// Get the device information data for each matching device.         
				DeviceInfoData[] diData = GetDeviceInfoData(diSetHandle);
				// Find the index of our instance. i.e. the touchpad mouse - I have 3 mice attached...    
				int index = GetIndexOfInstance(diSetHandle, diData, instanceId);
				// Disable...          
				EnableDevice(diSetHandle, diData[index], enable);
			}
			finally
			{
				if (diSetHandle != null)
				{
					if (diSetHandle.IsClosed == false)
					{
						diSetHandle.Close();
					} diSetHandle.Dispose();
				}
			}
		}

		private static DeviceInfoData[] GetDeviceInfoData(SafeDeviceInfoSetHandle handle)
		{
			List<DeviceInfoData> data = new List<DeviceInfoData>();
			DeviceInfoData did = new DeviceInfoData(); int didSize = Marshal.SizeOf(did);
			did.Size = didSize; int index = 0;
			while (NativeMethods.SetupDiEnumDeviceInfo(handle, index, ref did))
			{
				data.Add(did); index += 1;
				did = new DeviceInfoData(); did.Size = didSize;
			} return data.ToArray();
		}

		/// <summary>
		/// Find the index of the particular DeviceInfoData for the instanceId.
		/// </summary>
		/// <param name="handle"></param>
		/// <param name="diData"></param>
		/// <param name="instanceId"></param>
		/// <returns></returns>
		private static int GetIndexOfInstance(SafeDeviceInfoSetHandle handle, DeviceInfoData[] diData, string instanceId)
		{
			const int ERROR_INSUFFICIENT_BUFFER = 122;
			for (int index = 0; index <= diData.Length - 1; index++)
			{
				StringBuilder sb = new StringBuilder(1);
				int requiredSize = 0;
				bool result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize); if (result == false && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
				{
					sb.Capacity = requiredSize;
					result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize);
				}
				if (result == false)
					throw new Win32Exception();
				if (instanceId.Equals(sb.ToString()))
				{
					return index;
				}
			}
			// not found           
			return -1;
		}

		/// <summary>
		///  enable/disable...
		/// </summary>
		/// <param name="handle"></param>
		/// <param name="diData"></param>
		/// <param name="enable"></param>
		private static void EnableDevice(SafeDeviceInfoSetHandle handle, DeviceInfoData diData, bool enable)
		{
			PropertyChangeParameters @params = new PropertyChangeParameters();
			// The size is just the size of the header, but we've flattened the structure.     
			// The header comprises the first two fields, both integer.          
			@params.Size = 8; @params.DiFunction = DiFunction.PropertyChange;
			@params.Scope = Scopes.Global; if (enable)
			{
				@params.StateChange = StateChangeAction.Enable;
			}
			else
			{
				@params.StateChange = StateChangeAction.Disable;
			}
			bool result = NativeMethods.SetupDiSetClassInstallParams(handle, ref diData, ref @params, Marshal.SizeOf(@params));
			if (result == false) throw new Win32Exception();
			result = NativeMethods.SetupDiCallClassInstaller(DiFunction.PropertyChange, handle, ref diData);
			if (result == false)
			{
				int err = Marshal.GetLastWin32Error();
				if (err == (int)SetupApiError.NotDisableable)
					throw new ArgumentException("Device can't be disabled (programmatically or in Device Manager).");
				else if (err <= (int)SetupApiError.NoAssociatedClass && err >= (int)SetupApiError.OnlyValidateViaAuthenticode)
					throw new Win32Exception("SetupAPI error: " + ((SetupApiError)err).ToString());
				else throw new Win32Exception();
			}
		}
	}


	namespace Hardware
	{
		#region Unmanaged

		public class Native
		{
			[DllImport("user32.dll", CharSet = CharSet.Auto)]
			public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, DEV_BROADCAST_DEVICEINTERFACE NotificationFilter, UInt32 Flags);

			[DllImport("user32.dll", CharSet = CharSet.Auto)]
			public static extern uint UnregisterDeviceNotification(IntPtr hHandle);

			[DllImport("setupapi.dll", SetLastError = true)]
			public static extern IntPtr SetupDiGetClassDevs(ref Guid gClass, UInt32 iEnumerator, IntPtr hParent, UInt32 nFlags);

			[DllImport("setupapi.dll", SetLastError = true)]
			public static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);

			[DllImport("setupapi.dll", SetLastError = true)]
			public static extern bool SetupDiEnumDeviceInfo(IntPtr lpInfoSet, UInt32 dwIndex, SP_DEVINFO_DATA devInfoData);

			[DllImport("setupapi.dll", SetLastError = true)]
			public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, SP_DEVINFO_DATA DeviceInfoData, UInt32 Property, UInt32 PropertyRegDataType, StringBuilder PropertyBuffer, UInt32 PropertyBufferSize, IntPtr RequiredSize);

			[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
			public static extern bool SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, IntPtr ClassInstallParams, int ClassInstallParamsSize);

			[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
			public static extern Boolean SetupDiCallClassInstaller(UInt32 InstallFunction, IntPtr DeviceInfoSet, IntPtr DeviceInfoData);

			// Structure with information for RegisterDeviceNotification.
			[StructLayout(LayoutKind.Sequential)]
			public struct DEV_BROADCAST_HANDLE
			{
				public int dbch_size;
				public int dbch_devicetype;
				public int dbch_reserved;
				public IntPtr dbch_handle;
				public IntPtr dbch_hdevnotify;
				public Guid dbch_eventguid;
				public long dbch_nameoffset;
				public byte dbch_data;
				public byte dbch_data1;
			}

			// Struct for parameters of the WM_DEVICECHANGE message
			[StructLayout(LayoutKind.Sequential)]
			public class DEV_BROADCAST_DEVICEINTERFACE
			{
				public int dbcc_size;
				public int dbcc_devicetype;
				public int dbcc_reserved;
			}

			[StructLayout(LayoutKind.Sequential)]
			public struct DEV_BROADCAST_HDR
			{
				uint dbch_size;
				uint dbch_devicetype;
				uint dbch_reserved;
			}

			[StructLayout(LayoutKind.Sequential)]
			public struct DEV_BROADCAST_VOLUME
			{
				public uint dbcv_size;
				public uint dbcv_devicetype;
				public uint dbcv_reserved;
				public uint dbcv_unitmask;
				public ushort dbcv_flags;
			}
			//SP_DEVINFO_DATA
			[StructLayout(LayoutKind.Sequential)]
			public class SP_DEVINFO_DATA
			{
				public int cbSize;
				public Guid classGuid;
				public int devInst;
				public ulong reserved;
			};

			[StructLayout(LayoutKind.Sequential)]
			public class SP_DEVINSTALL_PARAMS
			{
				public int cbSize;
				public int Flags;
				public int FlagsEx;
				public IntPtr hwndParent;
				public IntPtr InstallMsgHandler;
				public IntPtr InstallMsgHandlerContext;
				public IntPtr FileQueue;
				public IntPtr ClassInstallReserved;
				public int Reserved;
				[MarshalAs(UnmanagedType.LPTStr)]
				public string DriverPath;
			};

			[StructLayout(LayoutKind.Sequential)]
			public class SP_PROPCHANGE_PARAMS
			{
				public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
				public int StateChange;
				public int Scope;
				public int HwProfile;
			};

			[StructLayout(LayoutKind.Sequential)]
			public class SP_CLASSINSTALL_HEADER
			{
				public int cbSize;
				public int InstallFunction;
			};

			//PARMS
			public const int DIGCF_ALLCLASSES = (0x00000004);
			public const int DIGCF_PRESENT = (0x00000002);
			public const int INVALID_HANDLE_VALUE = -1;
			public const int SPDRP_DEVICEDESC = (0x00000000);
			public const int MAX_DEV_LEN = 1000;
			public const int DEVICE_NOTIFY_WINDOW_HANDLE = (0x00000000);
			public const int DEVICE_NOTIFY_SERVICE_HANDLE = (0x00000001);
			public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = (0x00000004);
			public const int DBT_DEVTYP_DEVICEINTERFACE = (0x00000005);
			public const int DBT_DEVNODES_CHANGED = (0x0007);
			public const int WM_DEVICECHANGE = (0x0219);
			public const int DIF_PROPERTYCHANGE = (0x00000012);
			public const int DICS_FLAG_GLOBAL = (0x00000001);
			public const int DICS_FLAG_CONFIGSPECIFIC = (0x00000002);
			public const int DICS_ENABLE = (0x00000001);
			public const int DICS_DISABLE = (0x00000002);
		}

		#endregion

		public class HardwareHelper
		{
			Version m_Version = new Version(1, 0, 0);

			#region Public Methods

			//Name:     GetAll
			//Inputs:   none
			//Outputs:  string array
			//Errors:   This method may throw the following errors.
			//          Failed to enumerate device tree!
			//          Invalid handle!
			//Remarks:  This is code I cobbled together from a number of newsgroup threads
			//          as well as some C++ stuff I translated off of MSDN.  Seems to work.
			//          The idea is to come up with a list of devices, same as the device
			//          manager does.  Currently it uses the actual "system" names for the
			//          hardware.  It is also possible to use hardware IDs.  See the docs
			//          for SetupDiGetDeviceRegistryProperty in the MS SDK for more details.
			public string[] GetAll()
			{
				List<string> HWList = new List<string>();
				try
				{
					Guid myGUID = System.Guid.Empty;
					IntPtr hDevInfo = Native.SetupDiGetClassDevs(ref myGUID, 0, IntPtr.Zero, Native.DIGCF_ALLCLASSES | Native.DIGCF_PRESENT);
					if (hDevInfo.ToInt32() == Native.INVALID_HANDLE_VALUE)
					{
						throw new Exception("Invalid Handle");
					}
					Native.SP_DEVINFO_DATA DeviceInfoData;
					DeviceInfoData = new Native.SP_DEVINFO_DATA();
					DeviceInfoData.cbSize = 28;
					//is devices exist for class
					DeviceInfoData.devInst = 0;
					DeviceInfoData.classGuid = System.Guid.Empty;
					DeviceInfoData.reserved = 0;
					UInt32 i;
					StringBuilder DeviceName = new StringBuilder("");
					DeviceName.Capacity = Native.MAX_DEV_LEN;
					for (i = 0; Native.SetupDiEnumDeviceInfo(hDevInfo, i, DeviceInfoData); i++)
					{
						//Declare vars
						while (!Native.SetupDiGetDeviceRegistryProperty(hDevInfo,
																		DeviceInfoData,
																		Native.SPDRP_DEVICEDESC,
																		0,
																		DeviceName,
																		Native.MAX_DEV_LEN,
																		IntPtr.Zero))
						{
							//Skip
						}
						HWList.Add(DeviceName.ToString());
					}
					Native.SetupDiDestroyDeviceInfoList(hDevInfo);
				}
				catch (Exception ex)
				{
					throw new Exception("Failed to enumerate device tree!", ex);
				}
				return HWList.ToArray();
			}
			//Name:     SetDeviceState
			//Inputs:   string[],bool
			//Outputs:  bool
			//Errors:   This method may throw the following exceptions.
			//          Failed to enumerate device tree!
			//Remarks:  This is nearly identical to the method above except it
			//          tries to match the hardware description against the criteria
			//          passed in.  If a match is found, that device will the be
			//          enabled or disabled based on bEnable.
			public bool SetDeviceState(string[] match, bool bEnable)
			{
				try
				{
					Guid myGUID = System.Guid.Empty;
					IntPtr hDevInfo = Native.SetupDiGetClassDevs(ref myGUID, 0, IntPtr.Zero, Native.DIGCF_ALLCLASSES | Native.DIGCF_PRESENT);
					if (hDevInfo.ToInt32() == Native.INVALID_HANDLE_VALUE)
					{
						return false;
					}
					Native.SP_DEVINFO_DATA DeviceInfoData;
					DeviceInfoData = new Native.SP_DEVINFO_DATA();
					DeviceInfoData.cbSize = 28;
					//is devices exist for class
					DeviceInfoData.devInst = 0;
					DeviceInfoData.classGuid = System.Guid.Empty;
					DeviceInfoData.reserved = 0;
					UInt32 i;
					StringBuilder DeviceName = new StringBuilder("");
					DeviceName.Capacity = Native.MAX_DEV_LEN;
					for (i = 0; Native.SetupDiEnumDeviceInfo(hDevInfo, i, DeviceInfoData); i++)
					{
						//Declare vars
						while (!Native.SetupDiGetDeviceRegistryProperty(hDevInfo,
																		DeviceInfoData,
																		Native.SPDRP_DEVICEDESC,
																		0,
																		DeviceName,
																		Native.MAX_DEV_LEN,
																		IntPtr.Zero))
						{
							//Skip
						}
						bool bMatch = true;
						foreach (string search in match)
						{
							if (!DeviceName.ToString().ToLower().Contains(search.ToLower()))
							{
								bMatch = false;
								break;
							}
						}
						if (bMatch)
						{
							ChangeIt(hDevInfo, DeviceInfoData, bEnable);
						}
					}
					Native.SetupDiDestroyDeviceInfoList(hDevInfo);
				}
				catch (Exception ex)
				{
					throw new Exception("Failed to enumerate device tree!", ex);
				}
				return true;
			}
			//Name:     HookHardwareNotifications
			//Inputs:   Handle to a window or service, 
			//          Boolean specifying true if the handle belongs to a window
			//Outputs:  false if fail, otherwise true
			//Errors:   This method may log the following errors.
			//          NONE
			//Remarks:  Allow a window or service to receive ALL hardware notifications.
			//          NOTE: I have yet to figure out how to make this work properly
			//          for a service written in C#, though it kicks butt in C++.  At any
			//          rate, it works fine for windows forms in either.
			public bool HookHardwareNotifications(IntPtr callback, bool UseWindowHandle)
			{
				try
				{
					Native.DEV_BROADCAST_DEVICEINTERFACE dbdi = new Native.DEV_BROADCAST_DEVICEINTERFACE();
					dbdi.dbcc_size = Marshal.SizeOf(dbdi);
					dbdi.dbcc_reserved = 0;
					dbdi.dbcc_devicetype = Native.DBT_DEVTYP_DEVICEINTERFACE;
					if (UseWindowHandle)
					{
						Native.RegisterDeviceNotification(callback,
							dbdi,
							Native.DEVICE_NOTIFY_ALL_INTERFACE_CLASSES |
							Native.DEVICE_NOTIFY_WINDOW_HANDLE);
					}
					else
					{
						Native.RegisterDeviceNotification(callback,
							dbdi,
							Native.DEVICE_NOTIFY_ALL_INTERFACE_CLASSES |
							Native.DEVICE_NOTIFY_SERVICE_HANDLE);
					}
					return true;
				}
				catch (Exception ex)
				{
					string err = ex.Message;
					return false;
				}
			}
			//Name:     CutLooseHardareNotifications
			//Inputs:   handle used when hooking
			//Outputs:  None
			//Errors:   This method may log the following errors.
			//          NONE
			//Remarks:  Cleans up unmanaged resources.  
			public void CutLooseHardwareNotifications(IntPtr callback)
			{
				try
				{
					Native.UnregisterDeviceNotification(callback);
				}
				catch
				{
					//Just being extra cautious since the code is unmanged
				}
			}
			#endregion

			#region Private Methods

			//Name:     ChangeIt
			//Inputs:   pointer to hdev, SP_DEV_INFO, bool
			//Outputs:  bool
			//Errors:   This method may throw the following exceptions.
			//          Unable to change device state!
			//Remarks:  Attempts to enable or disable a device driver.  
			//          IMPORTANT NOTE!!!   This code currently does not check the reboot flag.
			//          =================   Some devices require you reboot the OS for the change
			//                              to take affect.  If this describes your device, you 
			//                              will need to look at the SDK call:
			//                              SetupDiGetDeviceInstallParams.  You can call it 
			//                              directly after ChangeIt to see whether or not you need 
			//                              to reboot the OS for you change to go into effect.
			private bool ChangeIt(IntPtr hDevInfo, Native.SP_DEVINFO_DATA devInfoData, bool bEnable)
			{
				try
				{
					//Marshalling vars
					int szOfPcp;
					IntPtr ptrToPcp;
					int szDevInfoData;
					IntPtr ptrToDevInfoData;

					Native.SP_PROPCHANGE_PARAMS pcp = new Native.SP_PROPCHANGE_PARAMS();
					if (bEnable)
					{
						pcp.ClassInstallHeader.cbSize = Marshal.SizeOf(typeof(Native.SP_CLASSINSTALL_HEADER));
						pcp.ClassInstallHeader.InstallFunction = Native.DIF_PROPERTYCHANGE;
						pcp.StateChange = Native.DICS_ENABLE;
						pcp.Scope = Native.DICS_FLAG_GLOBAL;
						pcp.HwProfile = 0;

						//Marshal the params
						szOfPcp = Marshal.SizeOf(pcp);
						ptrToPcp = Marshal.AllocHGlobal(szOfPcp);
						Marshal.StructureToPtr(pcp, ptrToPcp, true);
						szDevInfoData = Marshal.SizeOf(devInfoData);
						ptrToDevInfoData = Marshal.AllocHGlobal(szDevInfoData);

						if (Native.SetupDiSetClassInstallParams(hDevInfo, ptrToDevInfoData, ptrToPcp, Marshal.SizeOf(typeof(Native.SP_PROPCHANGE_PARAMS))))
						{
							Native.SetupDiCallClassInstaller(Native.DIF_PROPERTYCHANGE, hDevInfo, ptrToDevInfoData);
						}
						pcp.ClassInstallHeader.cbSize = Marshal.SizeOf(typeof(Native.SP_CLASSINSTALL_HEADER));
						pcp.ClassInstallHeader.InstallFunction = Native.DIF_PROPERTYCHANGE;
						pcp.StateChange = Native.DICS_ENABLE;
						pcp.Scope = Native.DICS_FLAG_CONFIGSPECIFIC;
						pcp.HwProfile = 0;
					}
					else
					{
						pcp.ClassInstallHeader.cbSize = Marshal.SizeOf(typeof(Native.SP_CLASSINSTALL_HEADER));
						pcp.ClassInstallHeader.InstallFunction = Native.DIF_PROPERTYCHANGE;
						pcp.StateChange = Native.DICS_DISABLE;
						pcp.Scope = Native.DICS_FLAG_CONFIGSPECIFIC;
						pcp.HwProfile = 0;
					}
					//Marshal the params
					szOfPcp = Marshal.SizeOf(pcp);
					ptrToPcp = Marshal.AllocHGlobal(szOfPcp);
					Marshal.StructureToPtr(pcp, ptrToPcp, true);
					szDevInfoData = Marshal.SizeOf(devInfoData);
					ptrToDevInfoData = Marshal.AllocHGlobal(szDevInfoData);
					Marshal.StructureToPtr(devInfoData, ptrToDevInfoData, true);

					bool rslt1 = Native.SetupDiSetClassInstallParams(hDevInfo, ptrToDevInfoData, ptrToPcp, Marshal.SizeOf(typeof(Native.SP_PROPCHANGE_PARAMS)));
					bool rstl2 = Native.SetupDiCallClassInstaller(Native.DIF_PROPERTYCHANGE, hDevInfo, ptrToDevInfoData);
					if ((!rslt1) || (!rstl2))
					{
						throw new Exception("Unable to change device state!");
					}
					else
					{
						return true;
					}
				}
				catch (Exception)
				{
					return false;
				}
			}

			#endregion
		}
	}

	public sealed class UsbEjector
	{
		private UsbEjector() { }

		internal const int WM_DEVICECHANGE = 0x0219;
		internal const int INVALID_HANDLE_VALUE = -1;
		internal const int FILE_SHARE_READ = 0x00000001;
		internal const int FILE_SHARE_WRITE = 0x00000002;
		internal const int OPEN_EXISTING = 3;
		//Constants for dwDesiredAccess:
		internal const UInt32 GENERIC_READ = 0x80000000;
		internal const UInt32 GENERIC_WRITE = 0x40000000;

		//errors 
		internal const int ERROR_NO_MORE_ITEMS = 259;
		internal const int ERROR_INSUFFICIENT_BUFFER = 122;
		internal const int ERROR_INVALID_DATA = 13;

		//guid controls usb
		internal const string GUID_DEVINTERFACE_VOLUME = "53f5630d-b6bf-11d0-94f2-00a0c91efb8b";
		internal const string GUID_DEVINTERFACE_DISK = "53f56307-b6bf-11d0-94f2-00a0c91efb8b";
		internal const int IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000;
		internal const int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 0x220410;
		internal const int IOCTL_USB_GET_NODE_CONNECTION_NAME = 0x220414;
		internal const int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = 0x220448;

		//setupapi
		internal const int DIGCF_PRESENT = (0x00000002);
		internal const int DIGCF_DEVICEINTERFACE = (0x00000010);

		internal const int SPDRP_DEVICEDESC = 0x00000000;
		internal const int SPDRP_CAPABILITIES = 0x0000000F;
		internal const int SPDRP_CLASS = 0x00000007;
		internal const int SPDRP_CLASSGUID = 0x00000008;
		internal const int SPDRP_FRIENDLYNAME = 0x0000000C;

		internal enum USB_CONNECTION_STATUS
		{
			NoDeviceConnected,
			DeviceConnected,
			DeviceFailedEnumeration,
			DeviceGeneralFailure,
			DeviceCausedOvercurrent,
			DeviceNotEnoughPower,
			DeviceNotEnoughBandwidth,
			DeviceHubNestedTooDeeply,
			DeviceInLegacyHub
		}

		internal enum USB_DEVICE_SPEED : byte
		{
			UsbLowSpeed,
			UsbFullSpeed,
			UsbHighSpeed
		}

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		internal struct USB_NODE_CONNECTION_INFORMATION_EX
		{
			public int ConnectionIndex;
			public USB_DEVICE_DESCRIPTOR DeviceDescriptor;
			public byte CurrentConfigurationValue;
			public byte Speed;
			public byte DeviceIsHub;
			public short DeviceAddress;
			public int NumberOfOpenPipes;
			public int ConnectionStatus;
			//public IntPtr PipeList;
		}

		[StructLayout(LayoutKind.Sequential)]
		internal struct _USB_NODE_CONNECTION_INFORMATION_EX
		{
			public int ConnectionIndex;
			public USB_DEVICE_DESCRIPTOR DeviceDescriptor;
			public byte CurrentConfigurationValue;
			public byte Speed;
			public byte DeviceIsHub;
			public ushort DeviceAddress;
			public int NumberOfOpenPipes;
			public USB_CONNECTION_STATUS ConnectionStatus;
			//public IntPtr PipeList;
		}

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		internal struct USB_DEVICE_DESCRIPTOR
		{
			public byte bLength;
			public byte bDescriptorType;
			public ushort bcdUSB;
			public byte bDeviceClass;
			public byte bDeviceSubClass;
			public byte bDeviceProtocol;
			public byte bMaxPacketSize0;
			public ushort idVendor;
			public ushort idProduct;
			public ushort bcdDevice;
			public byte iManufacturer;
			public byte iProduct;
			public byte iSerialNumber;
			public byte bNumConfigurations;
		}

		[StructLayout(LayoutKind.Sequential)]
		internal struct DISK_EXTENT
		{
			internal int DiskNumber;
			internal long StartingOffset;
			internal long ExtentLength;
		}

		internal enum PNP_VETO_TYPE
		{
			Ok,

			TypeUnknown,
			LegacyDevice,
			PendingClose,
			WindowsApp,
			WindowsService,
			OutstandingOpen,
			Device,
			Driver,
			IllegalDeviceRequest,
			InsufficientPower,
			NonDisableable,
			LegacyDriver,
		}

		[StructLayout(LayoutKind.Sequential)]
		internal class SP_DEVINFO_DATA
		{
			internal int cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
			internal Guid classGuid = Guid.Empty; // temp
			internal int devInst = 0; // dumy
			internal int reserved = 0;
		}

		[StructLayout(LayoutKind.Sequential, Pack = 2)]
		internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
		{
			internal int cbSize;
			internal short devicePath;
		}

		[StructLayout(LayoutKind.Sequential)]
		internal class SP_DEVICE_INTERFACE_DATA
		{
			internal int cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
			internal Guid interfaceClassGuid = Guid.Empty; // temp
			internal int flags = 0;
			internal int reserved = 0;
		}

		[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
		internal static extern bool GetVolumeNameForVolumeMountPoint(string volumeName, StringBuilder uniqueVolumeName, int uniqueNameBufferCapacity);

		[DllImport("Kernel32.dll", SetLastError = true)]
		internal static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
												uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

		[DllImport("Kernel32.dll", SetLastError = true)]
		internal static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer,
													int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);

		[DllImport("Kernel32.dll", SetLastError = true)]
		internal static extern bool CloseHandle(IntPtr hObject);

		[DllImport("setupapi.dll")]
		internal static extern int CM_Get_Parent(ref int pdnDevInst, int dnDevInst, int ulFlags);

		[DllImport("setupapi.dll")]
		internal static extern int CM_Get_Device_ID(int dnDevInst, StringBuilder buffer,
													int bufferLen, int ulFlags);

		[DllImport("setupapi.dll")]
		internal static extern int CM_Request_Device_Eject(int dnDevInst, out PNP_VETO_TYPE pVetoType,
															StringBuilder pszVetoName, int ulNameLength, int ulFlags);

		[DllImport("setupapi.dll", EntryPoint = "CM_Request_Device_Eject")]
		internal static extern int CM_Request_Device_Eject_NoUi(int dnDevInst, IntPtr pVetoType,
															StringBuilder pszVetoName, int ulNameLength, int ulFlags);


		[DllImport("setupapi.dll")]
		internal static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, int enumerator, IntPtr hwndParent, int flags);

		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
		internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData,
			ref Guid interfaceClassGuid, int memberIndex, SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

		[DllImport("setupapi.dll")]
		internal static extern bool SetupDiOpenDeviceInfo(IntPtr deviceInfoSet, string deviceInstanceId, IntPtr hwndParent,
															int openFlags, SP_DEVINFO_DATA deviceInfoData);

		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
		internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
																	IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize,
																	ref int requiredSize, SP_DEVINFO_DATA deviceInfoData);

		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
		internal static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData,
																	int property, out int propertyRegDataType, IntPtr propertyBuffer,
																	int propertyBufferSize, out int requiredSize);

		[DllImport("setupapi.dll")]
		internal static extern uint SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);

		public static void SomeMethod(string HubDevicePath,int hubPortCount)
		{
			// Open a handle to the Hub device
			IntPtr h = CreateFile(HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
			if (h.ToInt32() != INVALID_HANDLE_VALUE)
			{
				int nBytes = Marshal.SizeOf(typeof(USB_NODE_CONNECTION_INFORMATION_EX));
				IntPtr ptrNodeConnection = Marshal.AllocHGlobal(nBytes);

				// loop thru all of the ports on the hub
				// BTW: Ports are numbered starting at 1
				for (int i = 1; i <= hubPortCount; i++)
				{
					int nBytesReturned;
					USB_NODE_CONNECTION_INFORMATION_EX NodeConnection = new USB_NODE_CONNECTION_INFORMATION_EX();
					NodeConnection.ConnectionIndex = i;
					Marshal.StructureToPtr(NodeConnection, ptrNodeConnection, true);

					if (DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, ptrNodeConnection, nBytes, ptrNodeConnection, nBytes, out nBytesReturned, IntPtr.Zero))
					{
						NodeConnection = (USB_NODE_CONNECTION_INFORMATION_EX)Marshal.PtrToStructure(ptrNodeConnection, typeof(USB_NODE_CONNECTION_INFORMATION_EX));
						// do something here...
					}
				}
				Marshal.FreeHGlobal(ptrNodeConnection);
				CloseHandle(h);
			}
		}
	}
}

